﻿using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Windows.Forms;

namespace VisualPureBasicIDE.PureBasic
{
    /// <summary>
    /// Structure for collecting compiler informations
    /// </summary>
    [Serializable]
    public struct CompilerInfo
    {
        public string CompilerExecutable;
        public string MainSourceFile;
        public string MainExecutable;
        public System.Drawing.Icon MainIcon;
        public bool CreateDll;
        public bool Console;
        public bool InlineAsm;
        public bool UnicodeExecutable;
        public bool ThreadSafeExecutable;
        public bool XpSkinSupport;
        public bool VistaAdministratorMode;
        public bool VistaNoVirtualization;
        public bool OnErrorSupport;
        public bool UseDebugger;
    }

    /// <summary>
    /// Compiles a PureBasic source file with the given rules
    /// </summary>
    [Serializable]
    public class CodeCompiler
    {
        /// <summary>
        /// Starts compiling of the specified PureBasic source file
        /// </summary>
        /// <param name="compilerSettings">The compiler settings</param>
        /// <param name="textBox">The TextBox to use</param>
        /// <returns></returns>
        public bool StartCompiling(CompilerInfo compilerSettings, TextBox textBox)
        {
            Process compilerProcess;
            var arguments = new string[14];
            var finalCommandLineArguments = "";
            var nextFreeIndex = 0;
            textBox.Text = "";

            // Let's see what the compiler options are...
            if (compilerSettings.CompilerExecutable == null)
            {
                throw new ArgumentNullException("compilerSettings");
            }
            if (compilerSettings.MainSourceFile != null)
            {
                arguments[nextFreeIndex] = compilerSettings.MainSourceFile;
                nextFreeIndex++;
            }
            else
            {
                throw new ArgumentNullException("compilerSettings");
            }
            if (compilerSettings.MainExecutable != null)
            {
                arguments[nextFreeIndex] = "/EXE " + compilerSettings.MainExecutable;
                nextFreeIndex++;
                // If this argument is not provided, the program is compiled within the directory of the caller (in this case: Visual PureBasic IDE)
            }
            if (compilerSettings.MainIcon != null)
            {
                try
                {
                    var fs = new FileStream(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\app.ico", FileMode.Create, FileAccess.Write, FileShare.None);
                    compilerSettings.MainIcon.Save(fs);
                    fs.Close();
                }
                catch (Exception)
                {
                    return false;
                }
                arguments[nextFreeIndex] = "/ICON " + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\app.ico";
                nextFreeIndex++;
            }
            if (compilerSettings.CreateDll)
            {
                arguments[nextFreeIndex] = "/DLL";
                nextFreeIndex++;
            }
            if (compilerSettings.Console)
            {
                arguments[nextFreeIndex] = "/CONSOLE";
                nextFreeIndex++;
            }
            if (compilerSettings.InlineAsm)
            {
                arguments[nextFreeIndex] = "/INLINEASM";
                nextFreeIndex++;
            }
            if (compilerSettings.UnicodeExecutable)
            {
                arguments[nextFreeIndex] = "/UNICODE";
                nextFreeIndex++;
            }
            if (compilerSettings.ThreadSafeExecutable)
            {
                arguments[nextFreeIndex] = "/THREAD";
                nextFreeIndex++;
            }
            if (compilerSettings.XpSkinSupport)
            {
                arguments[nextFreeIndex] = "/XP";
                nextFreeIndex++;
            }
            if (compilerSettings.VistaAdministratorMode ^ compilerSettings.VistaNoVirtualization)
            {
                arguments[nextFreeIndex] = compilerSettings.VistaAdministratorMode ? "/ADMINISTRATOR" : "/USER";
            }
            else if (compilerSettings.VistaAdministratorMode)
            {
                throw new ArgumentException("VistaAdministratorMode and VistaNoVirtualization cannot be both true");
            }

            // The seperated arguments are "patched together" here
            for (var i = 0; i < arguments.Length; i++)
            {
                if (arguments[i] != null)
                {
                    finalCommandLineArguments += arguments[i];
                    if (arguments[i + 1] != null) finalCommandLineArguments += " ";
                }
                else break;
            }
            try
            {
                compilerProcess = new Process
                                      {
                                          StartInfo =
                                              {
                                                  FileName = compilerSettings.CompilerExecutable,
                                                  Arguments = finalCommandLineArguments,
                                                  CreateNoWindow = true
                                              },
                                          EnableRaisingEvents = true
                                      };

                compilerProcess.StartInfo.RedirectStandardError = true;
                compilerProcess.StartInfo.RedirectStandardOutput = true;
                compilerProcess.StartInfo.UseShellExecute = false;
                if (compilerProcess.Start())
                {
                    compilerProcess.WaitForExit();
                    Thread.Sleep(100);
                }
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }
    }
}